﻿using Caffeine.Core.Metadata;
using Caffeine.Core.Test.TestModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Caffeine.Core.Test
{
    [TestClass]
    public class ModelMetadataTester
    {
        [TestMethod]
        public void AssertMetadataIsCorrect()
        {
            List<Type> knownTypes = new List<Type>();
            knownTypes.Add(typeof(TestStruct));
            knownTypes.Add(typeof(EquatableTestStruct));
            knownTypes.Add(typeof(EnumForTesting));
            knownTypes.Add(typeof(List<Object>));
            knownTypes.Add(typeof(SerializableObject));
            knownTypes.Add(typeof(Person));

            IModelFactory factory = new ModelFactory();
            ModelForMetadataTestingB original = factory.CreateModel<ModelForMetadataTestingB>();
            original.Name = "Root";

            ModelMetadata metadata = original.GetMetadata();
            AssertMetadataIsCorrect(original, metadata);

            ModelForMetadataTesting copy = factory.CreateDeepCopy(original, knownTypes);
            AssertModelCopyIsCorrect(original, copy, true);

            metadata = copy.GetMetadata();
            AssertMetadataIsCorrect(copy, metadata);

            ModelForMetadataTesting copy2 = factory.CreateDeepCopy<Caffeine.Core.Test.TestModel.ModelForMetadataTesting>(copy, knownTypes);
            AssertModelCopyIsCorrect(copy, copy2, true);
            AssertModelCopyIsCorrect(original, copy2, true);

            original.EquatableTestStruct = new EquatableTestStruct() { IntProperty = 5 };
            original.NullableEquatableTestStruct = new EquatableTestStruct() { IntProperty = 7 };
            original.TestStruct = new TestStruct() { IntProperty = 3 };
            original.NullableTestStruct = new TestStruct() { IntProperty = 4 };
            original.EnumValue = EnumForTesting.TestValue2;
            original.NullableEnumValue = EnumForTesting.TestValue3;
            original.CharValue = 'C';
            original.NullableCharValue = 'B';
            original.StringValue = "TheStringValue";
            original.FirstName = "John";
            original.LastName = "Doe";
            original.PersonFirstName = "Jane";
            original.PersonLastName = "Doe";
            original.ObjectValue = new List<Object>();
            original.ChildValue = factory.CreateModel<ModelForMetadataTesting>();
            original.ChildValue.Name = "ChildValue";
            original.Child2Value = factory.CreateModel<ModelForMetadataTestingB>();
            original.Child2Value.Name = "Child2Value";
            original.Children.Add(factory.CreateModel<ModelForMetadataTesting>());
            original.Children[0].Name = "CollectionChild1Value";
            original.Children2.Add(factory.CreateModel<ModelForMetadataTestingB>());
            original.Children2[0].Name = "CollectionChild2Value";
            original.SerializableType = new SerializableObject();
            original.ExecuteWithStringParamCommand.CanExecuteResultOverride = false;
            original.ExecuteWithStringParamCommand.CanExecuteResultOverrideForNulls = false;
            original.ExecuteWithStringParamCommand.CanExecute(null);
            original.ExecuteWithObjectParamCommand.CanExecuteResultOverride = true;
            original.ExecuteWithObjectParamCommand.CanExecuteResultOverrideForNulls = true;
            original.ExecuteWithObjectParamCommand.CanExecute(null);

            metadata = original.GetMetadata();
            AssertMetadataIsCorrect(original, metadata);

            copy = factory.CreateDeepCopy(original, knownTypes);
            AssertModelCopyIsCorrect(original, copy, true);

            metadata = copy.GetMetadata();
            AssertMetadataIsCorrect(copy, metadata);

            copy2 = factory.CreateDeepCopy(copy, knownTypes);
            AssertModelCopyIsCorrect(original, copy2, true);
            AssertModelCopyIsCorrect(copy, copy2, true);
        }

        [TestMethod]
        public void CopyIntermediateChildTest()
        {
            List<Type> knownTypes = new List<Type>();
            knownTypes.Add(typeof(TestStruct));
            knownTypes.Add(typeof(EquatableTestStruct));
            knownTypes.Add(typeof(EnumForTesting));
            knownTypes.Add(typeof(List<Object>));
            knownTypes.Add(typeof(SerializableObject));
            knownTypes.Add(typeof(Person));

            IModelFactory factory = new ModelFactory();
            ModelForMetadataTesting root = factory.CreateModel<ModelForMetadataTesting>();
            ModelForMetadataTesting child1 = factory.CreateModel<ModelForMetadataTesting>();
            ModelForMetadataTesting child2 = factory.CreateModel<ModelForMetadataTesting>();
            ModelForMetadataTesting child3 = factory.CreateModel<ModelForMetadataTesting>();
            ModelForMetadataTesting child4 = factory.CreateModel<ModelForMetadataTesting>();

            root.Name = "Root";
            child1.Name = "Child1";
            child2.Name = "Child2";
            child3.Name = "Child3";
            child4.Name = "Child4";

            root.Children.Add(child1);
            child1.Children.Add(child2);
            child2.ChildValue = child3;
            child3.Children.Add(child4);

            Assert.IsTrue(child1.IsAddedToCollection);
            Assert.IsTrue(child1.HasParent);
            Assert.AreEqual(root, child1.Parent);
            Assert.AreEqual(root.Children, child1.ParentProperty);
            Assert.IsTrue(child1.IsAddedToCollection);
            
            ModelForMetadataTesting child1Copy = factory.CreateDeepCopy(child1, knownTypes);

            Assert.IsFalse(child1Copy.IsAddedToCollection);
            Assert.IsFalse(child1Copy.HasParent);
            Assert.IsNull(child1Copy.Parent);
            Assert.IsNull(child1Copy.ParentProperty);
            AssertModelCopyIsCorrect(child1, child1Copy, true);

            root.CommitGraph();
            child1Copy = factory.CreateDeepCopy(child1, knownTypes);

            AssertModelCopyIsCorrect(child1, child1Copy, true);

            child1Copy.Children[0].StringValue = "Value";
            Assert.IsFalse(child1Copy.IsDirty);
            Assert.IsTrue(child1Copy.IsDirtyWithin);
            Assert.IsFalse(root.IsDirty);
            Assert.IsFalse(root.IsDirtyWithin);
            root.Children.Add(child1Copy, true);
            Assert.IsFalse(root.IsDirty);
            Assert.IsTrue(root.IsDirtyWithin);

            ModelForMetadataTesting rootCopy = factory.CreateDeepCopy(root, knownTypes);
            AssertModelCopyIsCorrect(root, rootCopy, true);
        }

        private static void AssertMetadataIsCorrect(ModelBase model, ModelMetadata metadata)
        {
            Assert.AreEqual(model.IsDirty, metadata.IsDirty);
            Assert.AreEqual(model.IsDirtyWithin, metadata.IsDirtyWithin);
            Assert.AreEqual(model.IsInvalid, metadata.IsInvalid);
            Assert.AreEqual(model.IsInvalidWithin, metadata.IsInvalidWithin);
            Assert.AreEqual(model.HasParent, metadata.HasParent);
            Assert.AreEqual(model.IsRemovedFromParent, metadata.IsRemovedFromParent);
            Assert.AreEqual(model.IsAddedToCollection, metadata.IsAddedToCollection);
            Assert.AreEqual(model.IsCommittedToCollection, metadata.IsCommittedToCollection);
            Assert.AreEqual(model.IsRemovedFromCollection, metadata.IsRemovedFromCollection);
            Assert.AreEqual(model.IsAssignedToParentProperty, metadata.IsAssignedToParentProperty);
            Assert.AreEqual(model.IsCommittedToParentProperty, metadata.IsCommittedToParentProperty);
            Assert.AreEqual(model.IsRemovedFromParentProperty, metadata.IsRemovedFromParentProperty);
            Assert.AreEqual(model.DirtyPropertyCount, metadata.DirtyPropertyCount);
            Assert.AreEqual(model.InvalidPropertyCount, metadata.InvalidPropertyCount);
            Assert.AreEqual(model.Name, metadata.Name);
            Assert.AreEqual(model.IsParentDirtyWithin, metadata.IsParentDirtyWithin);
            Assert.AreEqual(model.IsParentInvalidWithin, metadata.IsParentInvalidWithin);
            Assert.AreEqual(model.DirtyWithinPropertyCount, metadata.DirtyWithinPropertyCount);
            Assert.AreEqual(model.InvalidWithinPropertyCount, metadata.InvalidWithinPropertyCount);

            Assert.AreNotEqual(model.ExtensibleData, metadata.ExtensibleData);

            if (model.ExtensibleData.Count == 0)
            {
                Assert.IsNull(metadata.ExtensibleData);
            }
            else
            {
                Assert.AreEqual(model.ExtensibleData.Count, metadata.ExtensibleData.Count);

                for (Int32 index = 0; index < model.ExtensibleData.Count; index++)
                {
                    Assert.AreEqual(model.ExtensibleData.ElementAt(index).Key, metadata.ExtensibleData.ElementAt(index).Key);
                    Assert.AreEqual(model.ExtensibleData.ElementAt(index).Value, metadata.ExtensibleData.ElementAt(index).Value);
                }
            }

            if (model.Properties.Count(property => !property.IsModelCollectionProperty) == 0)
            {
                Assert.IsNull(metadata.ScalarProperties);
            }
            else
            {
                Assert.AreEqual(model.Properties.Count(property => !property.IsModelCollectionProperty), metadata.ScalarProperties.Count());
            }

            if (model.Properties.Count(property => property.IsModelCollectionProperty) == 0)
            {
                Assert.IsNull(metadata.CollectionProperties);
            }
            else
            {
                Assert.AreEqual(model.Properties.Count(property => property.IsModelCollectionProperty), metadata.CollectionProperties.Count());
            }

            for (Int32 propertyIndex = 0; propertyIndex < model.Properties.Count(); propertyIndex++)
            {
                ModelPropertyBase property = model.Properties.ElementAt(propertyIndex);

                if (!property.IsModelCollectionProperty)
                {
                    ModelScalarPropertyMetadata propertyMetadata = metadata.ScalarProperties.Single(md => md.Name == property.Name);
                    Assert.AreEqual(propertyMetadata.PropertyIndex, propertyIndex);
                    AssertPropertyMetadata(property as ModelScalarPropertyBase, propertyMetadata);
                }
                else
                {
                    ModelCollectionMetadata propertyMetadata = metadata.CollectionProperties.Single(md => md.Name == property.Name);
                    Assert.AreEqual(propertyMetadata.PropertyIndex, propertyIndex);
                    AssertCollectionMetadata(property as ModelCollectionBase, propertyMetadata);
                }
            }

            if (model.Commands.Count() == 0)
            {
                Assert.IsNull(metadata.Commands);
            }
            else
            {
                Assert.AreEqual(model.Commands.Count(), metadata.Commands.Count());

                for (Int32 index = 0; index < model.Commands.Count(); index++)
                {
                    AssertCommandMetadata(model.Commands.ElementAt(index), metadata.Commands[index]);
                }
            }
        }

        private static void AssertPropertyMetadata(ModelScalarPropertyBase property, ModelScalarPropertyMetadata metadata)
        {
            if (!property.IsModelProperty)
            {
                Assert.AreEqual(property.GetValueForTesting(), metadata.Value);

                if (property.IsDirty)
                {
                    Assert.AreEqual(property.GetCommittedValueForTesting(), metadata.CommittedValue);
                }
                else
                {
                    Assert.IsNull(metadata.CommittedValue);
                }
            }
            else
            {
                if (property.GetValueForTesting() != null)
                {
                    AssertMetadataIsCorrect(property.GetValueForTesting() as ModelBase, metadata.Value as ModelMetadata);
                }
                else
                {
                    Assert.IsNull(metadata.Value);
                }

                if (property.IsDirty)
                {
                    if (property.GetCommittedValueForTesting() != null)
                    {
                        AssertMetadataIsCorrect(property.GetCommittedValueForTesting() as ModelBase, metadata.CommittedValue as ModelMetadata);
                    }
                    else
                    {
                        Assert.IsNull(metadata.CommittedValue);
                    }
                }
                else
                {
                    Assert.IsNull(metadata.CommittedValue);
                }
            }

            Assert.AreEqual(property.IsDefault, metadata.IsDefault);
            Assert.AreEqual(property.UpdateSyncOnCommit, metadata.UpdateSyncOnCommit);
            Assert.AreEqual(property.AllowSync, metadata.AllowSync);
            AssertPropertyBaseMetadata(property, metadata);
        }

        private static void AssertCollectionMetadata(ModelCollectionBase collection, ModelCollectionMetadata metadata)
        {
            if (collection.Count > 0)
            {
                Assert.AreEqual(collection.Count, metadata.Children.Length);

                Int32 index = 0;

                foreach (ModelBase model in collection)
                {
                    AssertMetadataIsCorrect(model, metadata.Children[index++]);
                }
            }
            else
            {
                Assert.IsNull(metadata.Children);
            }

            if (collection.RemovedModelCount > 0)
            {
                Assert.AreEqual(collection.RemovedModelCount, metadata.RemovedChildren.Length);
                Int32 index = 0;

                foreach (ModelBase model in collection.GetRemovedModels())
                {
                    AssertMetadataIsCorrect(model, metadata.RemovedChildren[index++]);
                }
            }
            else
            {
                Assert.IsNull(metadata.RemovedChildren);
            }

            AssertPropertyBaseMetadata(collection, metadata);
        }

        private static void AssertPropertyBaseMetadata(ModelPropertyBase property, ModelPropertyBaseMetadata metadata)
        {
            Assert.AreEqual(property.Name, metadata.Name);
            Assert.AreEqual(property.IsDirty, metadata.IsDirty);
            Assert.AreEqual(property.IsInvalid, metadata.IsInvalid);

            if (property.ValidationErrors == null || property.ValidationErrors.Count() == 0)
            {
                Assert.IsNull(metadata.ValidationErrors);
            }
            else
            {
                Assert.AreEqual(property.ValidationErrors.Count(), metadata.ValidationErrors.Count());

                for (Int32 index = 0; index < property.ValidationErrors.Count(); index++)
                {
                    ValidationError validationError = property.ValidationErrors.ElementAt(index);
                    ValidationErrorMetadata validationErrorMetadata = metadata.ValidationErrors.ElementAt(index);

                    Assert.AreEqual(validationError.ErrorCode, validationErrorMetadata.ErrorCode);
                    Assert.AreEqual(validationError.ErrorDescription, validationErrorMetadata.ErrorDescription);
                }
            }
        }

        private static void AssertCommandMetadata(ModelCommandBase command, ModelCommandMetadata metadata)
        {
            Assert.AreEqual(command.CanExecuteResult, metadata.CanExecuteResult);
            Assert.AreEqual(command.CanExecuteResultOverride, metadata.CanExecuteResultOverride);
            Assert.AreEqual(command.CanExecuteResultOverrideForNulls, metadata.CanExecuteResultOverrideForNulls);
        }

        private static void AssertModelCopyIsCorrect(ModelBase model, ModelBase modelCopy, bool isRootOfCopy)
        {
            Assert.IsNotNull(modelCopy.PropertyChangeCounter);
            Assert.IsNotNull(modelCopy.ParentFactory);
            Assert.AreEqual(model.IsDirty, modelCopy.IsDirty);
            Assert.AreEqual(model.IsDirtyWithin, modelCopy.IsDirtyWithin);
            Assert.AreEqual(model.IsInvalid, modelCopy.IsInvalid);
            Assert.AreEqual(model.IsInvalidWithin, modelCopy.IsInvalidWithin);

            if (isRootOfCopy)
            {
                Assert.IsFalse(modelCopy.HasParent);
                Assert.IsFalse(modelCopy.IsRemovedFromParent);
                Assert.IsFalse(modelCopy.IsAddedToCollection);
                Assert.IsFalse(modelCopy.IsCommittedToCollection);
                Assert.IsFalse(modelCopy.IsRemovedFromCollection);
                Assert.IsFalse(modelCopy.IsAssignedToParentProperty);
                Assert.IsFalse(modelCopy.IsCommittedToParentProperty);
                Assert.IsFalse(modelCopy.IsRemovedFromParentProperty);
            }
            else
            {
                Assert.AreEqual(model.HasParent, modelCopy.HasParent);
                Assert.AreEqual(model.IsRemovedFromParent, modelCopy.IsRemovedFromParent);
                Assert.AreEqual(model.IsAddedToCollection, modelCopy.IsAddedToCollection);
                Assert.AreEqual(model.IsCommittedToCollection, modelCopy.IsCommittedToCollection);
                Assert.AreEqual(model.IsRemovedFromCollection, modelCopy.IsRemovedFromCollection);
                Assert.AreEqual(model.IsAssignedToParentProperty, modelCopy.IsAssignedToParentProperty);
                Assert.AreEqual(model.IsCommittedToParentProperty, modelCopy.IsCommittedToParentProperty);
                Assert.AreEqual(model.IsRemovedFromParentProperty, modelCopy.IsRemovedFromParentProperty);
            }
            
            Assert.AreEqual(model.IsInitialized, modelCopy.IsInitialized);
            Assert.AreEqual(model.DirtyPropertyCount, modelCopy.DirtyPropertyCount);
            Assert.AreEqual(model.InvalidPropertyCount, modelCopy.InvalidPropertyCount);
            Assert.AreEqual(model.Name, modelCopy.Name);
            Assert.AreEqual(model.IsParentDirtyWithin, modelCopy.IsParentDirtyWithin);
            Assert.AreEqual(model.IsParentInvalidWithin, modelCopy.IsParentInvalidWithin);
            Assert.AreEqual(model.DirtyWithinPropertyCount, modelCopy.DirtyWithinPropertyCount);
            Assert.AreEqual(model.InvalidWithinPropertyCount, modelCopy.InvalidWithinPropertyCount);

            Assert.AreEqual(model.ExtensibleData.Count, modelCopy.ExtensibleData.Count);

            for (Int32 index = 0; index < model.ExtensibleData.Count; index++)
            {
                Assert.AreEqual(model.ExtensibleData.ElementAt(index).Key, modelCopy.ExtensibleData.ElementAt(index).Key);
                Assert.AreEqual(model.ExtensibleData.ElementAt(index).Value, modelCopy.ExtensibleData.ElementAt(index).Value);
            }

            if (model.Parent == null || isRootOfCopy)
            {
                Assert.IsNull(modelCopy.Parent);
                Assert.IsNull(modelCopy.ParentProperty);
            }
            else
            {
                Assert.AreEqual(model.Parent.Name, modelCopy.Parent.Name);
                Assert.AreEqual(model.ParentProperty.Name, modelCopy.ParentProperty.Name);
            }

            Assert.AreEqual(model.Properties.Count(), modelCopy.Properties.Count());

            for (Int32 index = 0; index < model.Properties.Count(); index++)
            {
                ModelPropertyBase property = model.Properties.ElementAt(index);
                ModelPropertyBase propertyCopy = modelCopy.Properties.ElementAt(index);

                if (!property.IsModelCollectionProperty)
                {
                    AssertPropertyCopy(property as ModelScalarPropertyBase, propertyCopy as ModelScalarPropertyBase);
                }
                else
                {
                    AssertCollectionCopy(property as ModelCollectionBase, propertyCopy as ModelCollectionBase);
                }
            }

            Assert.AreEqual(model.Commands.Count(), modelCopy.Commands.Count());

            for (Int32 index = 0; index < model.Commands.Count(); index++)
            {
                AssertCommandCopy(model.Commands.ElementAt(index), modelCopy.Commands.ElementAt(index));
            }
        }

        private static void AssertPropertyCopy(ModelScalarPropertyBase property, ModelScalarPropertyBase propertyCopy)
        {
            if (!property.IsModelProperty)
            {
                if (
                        !(property is Caffeine.Core.ModelProperty.UnconstrainedProperty<Object>) &&
                        !(property is Caffeine.Core.ModelProperty.UnconstrainedProperty<SerializableObject>) &&
                        !(property is Caffeine.Core.ModelProperty.ComputedModelProperty<String, Person>)
                   )
                {
                    Assert.AreEqual(property.GetValueForTesting(), propertyCopy.GetValueForTesting());
                    Assert.AreEqual(property.GetCommittedValueForTesting(), propertyCopy.GetCommittedValueForTesting());
                }
                else
                {
                    if (property.GetValueForTesting() == null)
                    {
                        Assert.IsNull(property.GetValueForTesting());
                    }
                    else
                    {
                        Assert.IsFalse(Object.ReferenceEquals(property.GetValueForTesting(), propertyCopy.GetValueForTesting()));
                    }

                    if (property.GetCommittedValueForTesting() == null)
                    {
                        Assert.IsNull(property.GetCommittedValueForTesting());
                    }
                    else
                    {
                        Assert.IsFalse(Object.ReferenceEquals(property.GetCommittedValueForTesting(), propertyCopy.GetCommittedValueForTesting()));
                    }
                }
            }
            else
            {
                if (property.GetValueForTesting() != null)
                {
                    AssertModelCopyIsCorrect
                    (
                        property.GetValueForTesting() as ModelBase, 
                        propertyCopy.GetValueForTesting() as ModelBase,
                        false
                    );
                }
                else
                {
                    Assert.IsNull(propertyCopy.GetValueForTesting());
                }

                if (property.GetCommittedValueForTesting() != null)
                {
                    if (property.IsDirty)
                    {
                        AssertModelCopyIsCorrect
                        (
                            property.GetCommittedValueForTesting() as ModelBase,
                            propertyCopy.GetCommittedValueForTesting() as ModelBase,
                            false
                        );
                    }
                    else
                    {
                        Assert.AreEqual(propertyCopy.GetValueForTesting(), propertyCopy.GetCommittedValueForTesting());
                    }
                }
                else
                {
                    Assert.IsNull(propertyCopy.GetCommittedValueForTesting());
                }
            }

            if (property.GetCompareMethodForTesting() != null)
            {
                Assert.IsFalse
                (
                    Object.ReferenceEquals
                    (
                        property.GetCompareMethodForTesting(),
                        propertyCopy.GetCompareMethodForTesting()
                    )
                );
            }
            else
            {
                Assert.IsNull(propertyCopy.GetCompareMethodForTesting());
            }

            if (property.GetValidationMethodForTesting() != null)
            {
                Assert.IsFalse
                (
                    Object.ReferenceEquals
                    (
                        property.GetValidationMethodForTesting(),
                        propertyCopy.GetValidationMethodForTesting()
                    )
                );
            }
            else
            {
                Assert.IsNull(propertyCopy.GetValidationMethodForTesting());
            }

            if (property.GetPropertySyncForTesting() != null)
            {
                Assert.IsFalse
                (
                    Object.ReferenceEquals
                    (
                        property.GetPropertySyncForTesting(),
                        propertyCopy.GetPropertySyncForTesting()
                    )
                );
            }
            else
            {
                Assert.IsNull(propertyCopy.GetPropertySyncForTesting());
            }

            Assert.AreEqual(property.IsDefault, propertyCopy.IsDefault);
            Assert.AreEqual(property.UpdateSyncOnCommit, propertyCopy.UpdateSyncOnCommit);
            Assert.AreEqual(property.AllowSync, propertyCopy.AllowSync);
            Assert.AreEqual(property.IsDefaultWrappedName, propertyCopy.IsDefaultWrappedName);
            Assert.AreEqual(property.CommittedValueWrappedName, propertyCopy.CommittedValueWrappedName);
            AssertPropertyBaseCopy(property, propertyCopy);
        }

        private static void AssertCollectionCopy(ModelCollectionBase collection, ModelCollectionBase collectionCopy)
        {
            Assert.IsNull(collectionCopy.GetCollectionChangedForTesting());
            Assert.AreEqual(collection.IsReadOnly, collectionCopy.IsReadOnly);
            Assert.AreEqual(collection.ContainsAddedModels, collectionCopy.ContainsAddedModels);
            Assert.AreEqual(collection.ContainsRemovedModels, collectionCopy.ContainsRemovedModels);
            Assert.AreEqual(collection.ContainsCommittedModels, collectionCopy.ContainsCommittedModels);
            Assert.AreEqual(collection.Count, collectionCopy.Count);
            Assert.AreEqual(collection.AddedModelCount, collectionCopy.AddedModelCount);
            Assert.AreEqual(collection.RemovedModelCount, collectionCopy.RemovedModelCount);
            Assert.AreEqual(collection.CommittedModelCount, collectionCopy.CommittedModelCount);
            Assert.AreEqual(collection.PropertyType, collectionCopy.PropertyType);
            Assert.AreEqual(collection.SupportsPropertyChangeTracking, collectionCopy.SupportsPropertyChangeTracking);

            if (collection.GetValidationMethodForTesting() != null)
            {
                Assert.IsNotNull(collectionCopy.GetValidationMethodForTesting());
            }
            else
            {
                Assert.IsNull(collectionCopy.GetValidationMethodForTesting());
            }

            for (Int32 index = 0; index < collection.AddedModelCount; index++)
            {
                AssertModelCopyIsCorrect
                (
                    collection.GetAddedModels().ElementAt(index),
                    collectionCopy.GetAddedModels().ElementAt(index),
                    false
                );
            }

            for (Int32 index = 0; index < collection.RemovedModelCount; index++)
            {
                AssertModelCopyIsCorrect
                (
                    collection.GetRemovedModels().ElementAt(index),
                    collectionCopy.GetRemovedModels().ElementAt(index),
                    false
                );
            }

            for (Int32 index = 0; index < collection.CommittedModelCount; index++)
            {
                AssertModelCopyIsCorrect
                (
                    collection.GetCommittedModels().ElementAt(index),
                    collectionCopy.GetCommittedModels().ElementAt(index),
                    false
                );
            }

            AssertPropertyBaseCopy(collection, collectionCopy);
        }

        private static void AssertPropertyBaseCopy(ModelPropertyBase property, ModelPropertyBase propertyCopy)
        {
            Assert.IsNull(propertyCopy.GetPropertyChangedForTesting());
            Assert.AreEqual(property.Parent.Name, propertyCopy.Parent.Name);
            Assert.AreEqual(property.Name, propertyCopy.Name);
            Assert.AreEqual(property.IsWrapped, propertyCopy.IsWrapped);
            Assert.AreEqual(property.Behavior, propertyCopy.Behavior);
            Assert.AreEqual(property.IsDirty, propertyCopy.IsDirty);
            Assert.AreEqual(property.IsInvalid, propertyCopy.IsInvalid);
            Assert.AreEqual(property.PropertyType, propertyCopy.PropertyType);
            Assert.AreEqual(property.IsModelProperty, propertyCopy.IsModelProperty);
            Assert.AreEqual(property.IsModelCollectionProperty, propertyCopy.IsModelCollectionProperty);
            Assert.AreEqual(property.IsDirtyWrappedName, propertyCopy.IsDirtyWrappedName);
            Assert.AreEqual(property.IsInvalidWrappedName, propertyCopy.IsInvalidWrappedName);
            Assert.AreEqual(property.ValidationErrorsWrappedName, propertyCopy.ValidationErrorsWrappedName);
            Assert.AreEqual(property.WrappedPropertyName, propertyCopy.WrappedPropertyName);

            if (property.ValidationErrors == null || property.ValidationErrors.Count() == 0)
            {
                Assert.IsNull(propertyCopy.ValidationErrors);
            }
            else
            {
                Assert.AreEqual(property.ValidationErrors.Count(), propertyCopy.ValidationErrors.Count());

                for (Int32 index = 0; index < property.ValidationErrors.Count(); index++)
                {
                    ValidationError validationErrorCopy = propertyCopy.ValidationErrors.ElementAt(index);
                    ValidationError validationError = property.ValidationErrors.ElementAt(index);

                    Assert.AreEqual(validationError.ErrorCode, validationErrorCopy.ErrorCode);
                    Assert.AreEqual(validationError.ErrorDescription, validationErrorCopy.ErrorDescription);
                }
            }
        }

        private static void AssertCommandCopy(ModelCommandBase command, ModelCommandBase commandCopy)
        {
            Assert.AreEqual(command.CanExecuteResult, commandCopy.CanExecuteResult);
            Assert.AreEqual(command.CanExecuteResultOverride, commandCopy.CanExecuteResultOverride);
            Assert.AreEqual(command.CanExecuteResultOverrideForNulls, commandCopy.CanExecuteResultOverrideForNulls);
        }
    }
}
